package com.yifeng.repo.base.security.auth.impl;

import com.gomcarter.frameworks.base.common.AssertUtils;
import com.gomcarter.frameworks.base.exception.CustomException;
import com.google.common.base.Strings;
import com.talkyun.utils.MD5;
import com.yifeng.base.account.api.AccountPermissionRestService;
import com.yifeng.repo.base.constant.BaseConstant;
import com.yifeng.repo.base.dto.PermissionDto;
import com.yifeng.repo.base.dto.UserDto;
import com.yifeng.repo.base.params.ConsoleParam;
import com.yifeng.repo.base.security.auth.constant.SecurityTypeEnum;
import com.yifeng.repo.base.security.console.impl.BusinessPermissionServiceImpl;
import com.yifeng.repo.base.security.context.manager.RequestContextManager;
import com.yifeng.repo.base.security.context.params.SecurityCheckParam;
import com.yifeng.repo.base.utils.action.BizCacheAction;
import com.yifeng.repo.base.utils.converter.JacksonHelper;
import lombok.extern.slf4j.Slf4j;

import java.util.List;
import java.util.Objects;

/**
 * Created by daibing on 2023/8/24.
 */
@Slf4j
public class BusinessSecurityChecker extends AbstractSecurityChecker {

    private SecurityCheckParam checkParam;
    private AccountPermissionRestService securityService;
    private BizCacheAction bizCacheAction;

    @Override
    public SecurityTypeEnum securityType() {
        return SecurityTypeEnum.BUSINESS;
    }

    @Override
    public void init(SecurityCheckParam checkParam, ConsoleParam consoleParam, BizCacheAction bizCacheAction) {
        this.checkParam = checkParam;
        this.securityService = new BusinessPermissionServiceImpl(consoleParam);
        this.bizCacheAction = bizCacheAction;
    }

    @Override
    protected List<String> listPublicKey() {
        AssertUtils.notNull(securityService, "未初始化2B鉴权服务！");
        return securityService.listPublicKey();
    }

    @Override
    protected boolean onlyGetTokenFromHeader() {
        if (checkParam == null) {
            return true;
        } else {
            return checkParam.isOnlyGetTokenFromHeader();
        }
    }

    @Override
    protected boolean getTokenFromSession() {
        if (checkParam == null) {
            return false;
        } else {
            return checkParam.isGetTokenFromSession();
        }
    }

    @Override
    public PermissionDto checkAndGetPermission(UserDto userDto) {
        // 1. 从redis拉取数据权限
        AssertUtils.notNull(securityService, "未初始化2B鉴权服务！");
        String key = bizCacheAction.redisKeyPrefix() + BaseConstant.REDIS_SEPARATOR + "permission"
                + BaseConstant.REDIS_SEPARATOR + userDto.getCode()
                + BaseConstant.REDIS_SEPARATOR + userDto.getIat();
        String permission = bizCacheAction.get(key);
        if (!Strings.isNullOrEmpty(permission)) {
            return JacksonHelper.toObj(permission, PermissionDto.class);
        }

        // 2. 从账号中心拉取数据权限，并且写入redis（2小时），重新登录生成新token就是新的截止时间
        try {
            PermissionDto permissionDto = securityService.getPermission(userDto.getCode());
            if (permissionDto != null) {
                bizCacheAction.put(key, 2 * 60 * 60, JacksonHelper.toJson(permissionDto));
            }
            return permissionDto;
        } catch (CustomException e) {
            // 调用账号中心加载权限失败，直接返回null，支持业务降级。
            log.warn("加载权限失败，用户编号是{}，error：", userDto.getCode(), e);
            return null;
        }
    }

    @Override
    public boolean checkApiSecured(UserDto userDto, String resource) {
        // 1. 从redis拉取接口权限
        AssertUtils.notNull(securityService, "未初始化2B鉴权服务！");
        String key = bizCacheAction.redisKeyPrefix() + BaseConstant.REDIS_SEPARATOR + "api"
                + BaseConstant.REDIS_SEPARATOR + userDto.getCode()
                + BaseConstant.REDIS_SEPARATOR + userDto.getIat()
                + BaseConstant.REDIS_SEPARATOR + MD5.md5(resource);
        String securedStr = bizCacheAction.get(key);
        if (!Strings.isNullOrEmpty(securedStr)) {
            return Boolean.parseBoolean(securedStr);
        }

        // 2. 从账号中心拉取接口检查结果，并且写入redis（2小时），重新登录生成新token就是新的截止时间
        try {
            boolean secured = securityService.checkApiSecured(userDto.getCode(), resource);
            bizCacheAction.put(key, 2 * 60 * 60, String.valueOf(secured));
            return secured;
        } catch (CustomException e) {
            // 调用账号中心检查权限失败（缓存操作失败异常不在该范围内），直接返回应用级别默认配置。
            log.warn("检查API接口权限失败，用户编号是{}，资源是{}，返回默认值{}，error：",
                    userDto.getCode(), resource, checkParam.isSuccessIfCheckSecuredException(), e);
            return checkParam.isSuccessIfCheckSecuredException();
        }
    }


    /**
     * 门店权限校验
     *
     * @param storeCodeList 验证的门店编码列表
     * @return true 验证通过
     */
    @Override
    public boolean checkStoreSecured(UserDto userDto, List<String> storeCodeList) {
        // 1. 如果门店编码为空 校验返回 false
        AssertUtils.notNull(securityService, "未初始化2B鉴权服务！");
        if (null == storeCodeList || storeCodeList.size() == 0) {
            return false;
        }

        // 2. 取当前所在门店校验
        PermissionDto permissionDto = RequestContextManager.getPermission();
        if (Objects.nonNull(permissionDto) && !Strings.isNullOrEmpty(permissionDto.getStoreCode()) && storeCodeList.size() == 1) {
            if (storeCodeList.contains(permissionDto.getStoreCode())) {
                return true;
            }
        }

        // 3. 从redis拉取数据权限
        String storeCodes = storeCodeList.stream().reduce((a, b) -> a + "," + b).orElse(null);
        String key = bizCacheAction.redisKeyPrefix() + BaseConstant.REDIS_SEPARATOR + "store"
                + BaseConstant.REDIS_SEPARATOR + userDto.getCode()
                + BaseConstant.REDIS_SEPARATOR + userDto.getIat()
                + BaseConstant.REDIS_SEPARATOR + MD5.md5(storeCodes);
        String securedStr = bizCacheAction.get(key);
        if (!Strings.isNullOrEmpty(securedStr)) {
            return Boolean.parseBoolean(securedStr);
        }

        // 4. 从权限控制源头拉取数据权限，并且写入redis（2小时），重新登录生成新token就是新的截止时间
        try {
            boolean secured = securityService.checkStoreSecured(userDto.getCode(), storeCodeList);
            bizCacheAction.put(key, 2 * 60 * 60, String.valueOf(secured));
            return secured;
        } catch (CustomException e) {
            // 调用账号中心检查权限失败（缓存操作失败异常不在该范围内），直接返回应用级别默认配置。
            log.warn("检查门店权限失败，用户编号是{}，门店列表是{}，返回默认值{}，error：",
                    userDto.getCode(), storeCodeList, checkParam.isSuccessIfCheckSecuredException(), e);
            return checkParam.isSuccessIfCheckSecuredException();
        }
    }

}
